package net.ncraft.mc.easyframework.aop.core;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.*;

public class MethodHandler implements MethodInterceptor {


    private final Object TARGET_INSTANCE;
    private final Set<Object> ASPECT_INSTANCES;
    private final Set<Method> ENHANCE_METHODS;
    private final Map<Method, Set<Method>> METHOD_MAP;

    public MethodHandler(Object targetInstance, Set<Object> aspectInstances, Set<Method> enhanceMethods, Map<Method, Set<Method>> methodMap) {
        TARGET_INSTANCE = targetInstance;
        ASPECT_INSTANCES = aspectInstances;
        ENHANCE_METHODS = enhanceMethods;
        METHOD_MAP = methodMap;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (!ENHANCE_METHODS.contains(method)) {
            return method.invoke(TARGET_INSTANCE, args);
        }
        Set<Method> aspectMethods = METHOD_MAP.get(method);
        Set<Object> aspectInstances = new HashSet<>();
        for (Method aspectMethod : aspectMethods) {
            for (Object aspectInstance : ASPECT_INSTANCES) {
                if (aspectInstance.getClass().isAssignableFrom(aspectMethod.getDeclaringClass())) {
                    aspectInstances.add(aspectInstance);
                }
            }
        }

        List<JoinPoint> joinPoints = new ArrayList<>();

        for (Method aspectMethod : aspectMethods) {
            Object aspectInstance = null;
            for (Object i : aspectInstances) {
                if (i.getClass().isAssignableFrom(aspectMethod.getDeclaringClass())) {
                    aspectInstance = i;
                    break;
                }
            }
            joinPoints.add(new JoinPoint(TARGET_INSTANCE, method, proxy, args, aspectInstance, aspectMethod));
        }
        JoinPoint joinPoint = buildJoinPoint(joinPoints);
        return joinPoint.getAspectMethod().invoke(joinPoint.getAspectInstance(),joinPoint);
    }


    private JoinPoint buildJoinPoint(List<JoinPoint> joinPoints) {
        //一级一级设置
        for (int i = 0; i < joinPoints.size(); i++) {
            JoinPoint joinPoint = joinPoints.get(i);
            if (i == joinPoints.size() - 1) {
                joinPoint.setNext(null);
            } else {
                joinPoint.setNext(joinPoints.get(i + 1));
            }
        }
        return joinPoints.get(0);
    }

}
